﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using MingYuanYun.Utility;

namespace ControlCenter.ApplicationService.Services.Utility
{
    /// <summary>
    /// Nupkg 文件操作类
    /// </summary>
    public class NupkgFile
    {
        /// <summary>
        /// 读取 .nupkg 包的描述信息
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static async Task<NuspecInfo> ReadNuspec(string filename)
        {
            var fileDict = await ReadTextFileContent(filename);
            var nuspecFileContent = fileDict["nuspec"];
            var configFileContent = string.Empty;
            fileDict.TryGetValue("config", out configFileContent);
            var nuspecInfo = ParseToNuspecInfo(nuspecFileContent);
            if (nuspecInfo != null && !string.IsNullOrEmpty(configFileContent))
            {
                nuspecInfo.ConfigItems = Utils.JsonToObject<List<NuspecConfigItem>>(configFileContent);
            }
            return nuspecInfo;
        }

        /// <summary>
        /// 从 .nupkg 包读取 .nuspec 描述文件内容
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        private static async Task<Dictionary<string, string>> ReadTextFileContent(string filename)
        {
            if (!File.Exists(filename))
            {
                throw new ApplicationException($"文件不存在 {filename}");
            }

            Dictionary<string, string> fileDict = new Dictionary<string, string>();

            using (ZipArchive zipArchive =
                ZipFile.Open(filename, ZipArchiveMode.Read))
            {
                foreach (ZipArchiveEntry entry in zipArchive.Entries)
                {
                    if (entry.Name.EndsWith(".nuspec"))
                    {
                        fileDict.Add("nuspec", await ReadEntryString(entry));
                    }
                    else if (entry.Name.ToLower().Equals("ini.config"))
                    {
                        fileDict.Add("config", await ReadEntryString(entry));
                    }
                }
            }
            return fileDict;
        }

        private static async Task<string> ReadEntryString(ZipArchiveEntry entry)
        {
            using (Stream stream = entry.Open())
            {
                StreamReader sr = new StreamReader(stream, Encoding.UTF8);
                return await sr.ReadToEndAsync();
            }
        }

        /// <summary>
        /// 解析XML，反序列化成 NuspecInfo 对象
        /// </summary>
        /// <param name="content">xml文档内容</param>
        /// <returns></returns>
        private static NuspecInfo ParseToNuspecInfo(string content)
        {
            if (string.IsNullOrEmpty(content))
            {
                return null;
            }

            try
            {
                NuspecInfo nuspecInfo = new NuspecInfo();
                var propDist = typeof(NuspecInfo).GetProperties().ToDictionary(p => p.Name);

                XmlDocument xdoc = new XmlDocument();
                xdoc.LoadXml(content);
                var e = xdoc.GetElementsByTagName("metadata");
                var n = e.Item(0).ChildNodes.GetEnumerator();
                while (n.MoveNext())
                {
                    var xelement = n.Current as XmlElement;
                    if (xelement == null)
                    {
                        continue;
                    }
                    if (propDist.TryGetValue(xelement.Name, out PropertyInfo prop))
                    {
                        if (prop.PropertyType == typeof(bool))
                        {
                            prop.SetValue(nuspecInfo, bool.Parse(xelement.InnerText));
                        }
                        else if (prop.PropertyType == typeof(string))
                        {
                            prop.SetValue(nuspecInfo, xelement.InnerText);
                        }
                        else if (prop.PropertyType == typeof(DateTime))
                        {
                            prop.SetValue(nuspecInfo, DateTime.Parse(xelement.InnerText));
                        }
                    }
                }

                return nuspecInfo;
            }
            catch (Exception exception)
            {
                return null;
            }
        }
    }

    /// <summary>
    /// Nupkg文件描述信息
    /// </summary>
    public class NuspecInfo
    {
        /// <summary>
        /// 包ID
        /// </summary>
        public string id { get; set; }
        /// <summary>
        /// 版本号
        /// </summary>
        public string version { get; set; }
        /// <summary>
        /// 作者
        /// </summary>
        public string authors { get; set; }
        /// <summary>
        /// 拥有者
        /// </summary>
        public string owners { get; set; }
        /// <summary>
        /// 需要授权
        /// </summary>
        public bool requireLicenseAcceptance { get; set; }
        /// <summary>
        /// 包描述
        /// </summary>
        public string description { get; set; }
        /// <summary>
        /// 发行说明
        /// </summary>
        public string releaseNotes { get; set; }

        /// <summary>
        /// 配置项
        /// </summary>
        public List<NuspecConfigItem> ConfigItems { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        public NuspecInfo()
        {
            this.ConfigItems = new List<NuspecConfigItem>();
        }
    }

    /// <summary>
    /// Nupkg 配置项
    /// </summary>
    public class NuspecConfigItem
    {
        /// <summary>
        /// 配置项名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 配置项类型
        /// </summary>
        public string Type { get; set; }
        /// <summary>
        /// 默认值
        /// </summary>
        public string DefaultValue { get; set; }
        /// <summary>
        /// 配置项描述
        /// </summary>
        public string Remark { get; set; }
    }
}
